/*
   Copyright 2009 Bloo. All Rights Reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package org.youtensils.client.plugin.applet;

import java.applet.Applet;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.AccessControlException;

import javax.swing.JFileChooser;
import javax.swing.UIManager;
import netscape.javascript.JSObject;

/**
 * An applet exposing Audio and Download capabilities to JavaScript.
 * @author Bobby Soares
 */
public class DownloadEngine extends Applet {
	private static final long serialVersionUID = 0;
	private static String homePath = null;
	public JSObject Win, Component;
	private DownloadMonitor downloadMonitor;
	
	public void init(){
		try{
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		}
		catch(Exception x1){}
		Win = JSObject.getWindow(this);
		Component = (JSObject) Win.call("getDownloadEngine", new Object[0]);
		if (Component == null) {
			System.out.println("A required JavaScript component is unavailable or has not yet loaded. ");
			System.out.println("Entering inactive state.");
			return;
		} else {
			System.out.println("Detected JavaScript component.");
			System.out.println("Entering active state.");
		}
		if(hasFullPermissions()){
			downloadMonitor = new DownloadMonitor(this);
			new Thread(downloadMonitor).start();
		}else{
			Component.call("onSandboxDetected", new Object[]{ });
		}
	}
	
	/**
	 * Returns a value indicating whether the applet is running with full permissions
	 * or inside a sandbox.
	 * To check for the sandbox, make a request to a page outside
	 * the applet's source domain.
	 * You can use this method to avoid applet hot-linking by pointing the web connection
	 * to a script that returns an HTTP error whenever the current domain differs from the
	 * expected value.
	 * @return a value indicating whether the applet is running with full permissions or inside a sandbox
	 */
	private boolean hasFullPermissions(){
		String ref = Win.getMember("location").toString();
		BufferedReader reader = null;
		try{
			HttpURLConnection.setFollowRedirects(true);
			URL url = new URL("http://verify.youtensils.org/detect/");
			HttpURLConnection webConnection = (HttpURLConnection)url.openConnection();
	        webConnection.setRequestProperty("Referer", ref);
	        webConnection.setRequestProperty("User-Agent", "Java/YouTensils/DownloadEngine");
	        webConnection.setUseCaches(false);
	        webConnection.setDoOutput(true);
	        webConnection.connect();
	        reader = new BufferedReader(new InputStreamReader(webConnection.getInputStream()));
	        String stat = reader.readLine();
	        if(stat.equalsIgnoreCase("ok")) return true;
	        else return false;
		}catch(AccessControlException x){
			System.out.println("Applet is running inside sandbox.");
		}catch(Exception x){
			x.printStackTrace();
		}finally{
			try{
				if(reader != null)
					reader.close();
			}catch(IOException x){}
		}
		return false;
	}
	
	public void destroy(){
		if(downloadMonitor != null) downloadMonitor.stop();
	}
	
	/**
	 * Presents the user with the open directory dialog for selecting the folder in
	 * which files are to be stored.
	 * @return the directory in the user's file system into which files will be downloaded
	 * @throws DownloadCancelationException if the user selected to cancel the open directory dialog
	 */
	public static String getHomeFolderPath() throws DownloadCancelationException {
		if(DownloadEngine.homePath != null) {
			File home = new File(DownloadEngine.homePath);
			if (home.exists() && home.isDirectory()) {
				return DownloadEngine.homePath;
			}
		}
		JFileChooser c = new JFileChooser();
		c.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		c.setMultiSelectionEnabled(false);
		c.setDialogTitle("Select the folder into which downloaded files should be saved");
		int rVal = c.showOpenDialog(null);
		if(rVal == JFileChooser.APPROVE_OPTION) {
			String folder = c.getSelectedFile().getAbsolutePath();
			if(folder.equals("")) folder = null;
			DownloadEngine.homePath = folder;
		}
		if(rVal == JFileChooser.CANCEL_OPTION){
			throw new DownloadCancelationException();
		}
		File home = new File(DownloadEngine.homePath);
		if(!home.exists() || !home.isDirectory())
			home.mkdir();
		return DownloadEngine.homePath;
	}
	
	/**
	 * Truncates and strips special characters from the supplied string.
	 * @param name the intended file name
	 * @return a file name of limited length not containing special characters
	 */
	public static String getValidFileName(String name){
		if(name == null) return "New File";
		name = name.replaceAll("[^ a-zA-Z0-9_\\-()&+@$#%!.,]", "");
		if(name == "") return "New File";
		if(name.length() > 40) name = name.substring(0, 40);
		return name;
	}
	
	/**
	 * Generates a unique file path under the user's selected home directory from given
	 * name and extension values. The generated value represents the path to a file that
	 * does not yet exist. If the user has not yet selected a home directory, an open
	 * directory dialog will be displayed.
	 * @param name the desired file name
	 * @param extension the desired file extension
	 * @return a new, unique and valid file path under the user's selected home directory
	 * @throws DownloadCancelationException if the user selected to cancel the open directory dialog
	 * @see org.youtensils.client.plugin.applet.DownloadEngine.getHomeFolderPath
	 * @see org.youtensils.client.plugin.applet.DownloadEngine.getValidFileName
	 */
	public static String getUniqueValidHomeFilePath(String name, String extension) throws DownloadCancelationException {
		String folderPath = DownloadEngine.getHomeFolderPath();
		String fileName = DownloadEngine.getValidFileName(name);
		File targetFile = new File(folderPath, fileName + "." + extension);
		if(targetFile.exists()){
			for(int i=1; i<1000; i++){
				targetFile = new File(folderPath, fileName + "-" + i + "." + extension);
				if(!targetFile.exists()) break;
			}
		}
		return targetFile.getPath();
	}
	
	public static void main(String[] args){
		try{
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		}
		catch(Exception x1){}
		(new DownloadEngine()).init();
	}
}
